﻿// Copyright 2007-2009 Stuart Caborn, Alex Scordellis
//  
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License. You may obtain a copy of
//  the License at
//  
//  http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//  License for the specific language governing permissions and limitations under
//  the License.
// 

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace NSynthesis.Reflection
{
    public static class Descriptors
    {
        public static string Description(this MethodInfo my)
        {
            return string.Format(CultureInfo.InvariantCulture,
                                 "{0} {1}::{2}{3}({4})",
                                 PrettyPrint(my.ReturnType),
                                 PrettyPrint(my.ReflectedType.RemoveProxy()),
                                 my.Name,
                                 my.GenericArguments(),
                                 my.ParameterList());
        }

        private static string PrettyPrint(Type type)
        {
            if (prettyPrintLookup.ContainsKey(type)) return prettyPrintLookup[type];
            if (type.IsGenericParameter) return type.Name;
            return type.FullName.RemoveTickMarks() + GenericArgumentsFor(type);
        }

        private static string RemoveTickMarks(this string stringWithTickMarks)
        {
            var match = new Regex("(.*)`.*?").Match(stringWithTickMarks);
            if(match.Success)
                return match.Groups[1].Value;
            return stringWithTickMarks;
        }

        private static string AsTypeParameters(this IEnumerable<Type> types)
        {
            return types.Aggregate(
                new StringBuilder("<"),
                (builder, genericType) => builder.AppendFormat("{0}, ", PrettyPrint(genericType)),
                builder => new Regex(", $").Replace(builder.ToString(), ">"));
        }

        private static string GenericArgumentsFor(Type type)
        {
            if (!type.IsGenericType)
                return string.Empty;

            return type.GetGenericArguments().AsTypeParameters();
        }

        private static string GenericArguments(this MethodBase method)
        {
            if (!method.IsGenericMethod)
                return string.Empty;

            return method.GetGenericArguments().AsTypeParameters();
        }

        private static string ParameterList(this MethodInfo methodInfo)
        {
            var parameterList = methodInfo.GetParameters().Aggregate(
                new StringBuilder(),
                (builder, info) => builder.AppendFormat("{0}, ", PrettyPrint(info.ParameterType)));
            if (parameterList.Length > 1) parameterList.Remove(parameterList.Length - 2, 2);
            return parameterList.ToString();
        }

        private static readonly Dictionary<Type, string> prettyPrintLookup = new Dictionary<Type, string>
                                                                                 {
                                                                                     {typeof (void), "void"},
                                                                                     {typeof (string), "string"},
                                                                                     {typeof (int), "int"},
                                                                                     {typeof (long), "long"},
                                                                                     {typeof (byte), "byte"},
                                                                                     {typeof (bool), "bool"},
                                                                                     {typeof (decimal), "decimal"},
                                                                                     {typeof (double), "double"},
                                                                                     {typeof (float), "float"},
                                                                                     {typeof (char), "char"},
                                                                                     {typeof (uint), "uint"},
                                                                                     {typeof (ulong), "ulong"},
                                                                                     {typeof (ushort), "ushort"},
                                                                                     {typeof (sbyte), "sbyte"},
                                                                                     {typeof (short), "short"},
                                                                                 };
    }
}